package com.wan;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.lang.caller.CallerUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.sun.istack.internal.Nullable;
import com.wan.core.event.Event;
import com.wan.core.event.EventBus;
import com.wan.core.shutdown.ShutdownManager;
import com.wan.setting.Conf;
import com.wan.core.strategy.StrategyPool;
import com.wan.thread.ExecutorManager;
import org.apache.logging.log4j.LogManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;

public final class AppContext {

    private static final Log log = LogFactory.get();
    private static AppContext APP_CONTEXT = null;
    private final String name;
    private final AtomicBoolean status;
    private final List<String> scanPackages = new ArrayList<>();
    private final Map<Kit, IKit> moduleMap = new HashMap<>();
    private final Set<Kit> noLoadKits = new HashSet<>();
    private final List<Event> startEvents = new ArrayList<>();
    private ApplicationContext context;
    private Function<String[], ApplicationContext> contextFunction;
    private boolean startOver = false;


    public AppContext(String name) {
        status = new AtomicBoolean(false);
        this.name = name;
        Conf.setReload(this::reloadConf);
        Conf conf = Conf.get();
        setInstance();
        ShutdownManager.addLast("logger", LogManager::shutdown);
    }

    public static AppContext test(Kit...kits){
        AppContext appContext = new AppContext("test");
        appContext.setOnlyLoadKits(kits);
        return appContext;
    }

    public static void testRun(@Nullable String scanPackage, Kit...kits){
        AppContext test = test(kits);
        if(scanPackage != null && !scanPackage.isEmpty()){
            test.addScanPackages(scanPackage);
        }
        test.start();
    }

    public static void testRun(Kit...kits){
        testRun(CallerUtil.getCallerCaller().getPackage().getName(),kits);
    }


    public static AppContext get() {
        return APP_CONTEXT;
    }

    public static <T> T getBean(Class<T> type) {
        return get().context.getBean(type);
    }

    public static <T> Map<String, T> getBeansByType(Class<T> superType) {
        return get().context.getBeansOfType(superType);
    }

    /**
     * 异步推送事件通知
     * 服务启动中的事件将会堆到启动完后统一发送
     */
    public static void publishEvent(Event event){
        AppContext appContext = get();
        if(appContext == null || appContext.startOver){
            EventBus.sendAsync(event);
        }else{
            appContext.startEvents.add(event);
        }
    }

    private void setInstance() {
        if (APP_CONTEXT == null) {
            synchronized (AppContext.class) {
                if (APP_CONTEXT == null) {
                    APP_CONTEXT = this;
                    return;
                }
            }
        }
        throw new RuntimeException("server context is exists !");
    }

    /**
     * 设置要扫描的包路径
     */
    public void addScanPackages(String... scanPackages) {
        this.scanPackages.addAll(Arrays.asList(scanPackages));
    }

    /**
     * 添加不需要加载的模块
     */
    public void addNoLoadKits(Kit... kits) {
        this.noLoadKits.addAll(Arrays.asList(kits));
    }

    /**
     * 设置仅加载的模块，不会叠加
     */
    public void setOnlyLoadKits(Kit... kits){
        this.noLoadKits.clear();
        for (Kit kit : Kit.values()) {
            if(Arrays.stream(kits).noneMatch(m -> m== kit)){
                noLoadKits.add(kit);
            }
        }
    }

    public void setContextBoot(Function<String[], ApplicationContext> function) {
        this.contextFunction = function;
    }

    /**
     * 启动
     */
    public synchronized void start() {
        TimeInterval timer = DateUtil.timer();
        if (status.compareAndSet(false, true)) {
            try {
                //本类所在包路径
                if(scanPackages.isEmpty()){
                    //实现类所在包路径
                    scanPackages.add(CallerUtil.getCallerCaller().getPackage().getName());
                }
                scanPackages.add(AppContext.class.getPackage().getName());
                String[] scanArray = scanPackages.toArray(new String[0]);
                //策略池预加载
                StrategyPool.preLoad(scanArray);
                for (Kit kit : Kit.values()) {
                    if (noLoadKits.contains(kit)) {
                        continue;
                    }
                    moduleMap.putIfAbsent(kit, loadModule(kit));
                }
                context = contextFunction == null ?
                        new AnnotationConfigApplicationContext(scanArray) :
                        contextFunction.apply(scanArray);
                Map<String, LifeCycle> beansOfType = context.getBeansOfType(LifeCycle.class);
                for (LifeCycle lifeCycle : beansOfType.values()) {
                    lifeCycle.started(this);
                }
                this.startOver = true;
                for (Event startEvent : this.startEvents) {
                    EventBus.sendAsync(startEvent);
                }
                log.info("Application Server [{}] 启动完成，耗时{}ms", name, timer.interval());
                clear();
                ShutdownManager.addShutdown(this::stop);
            } catch (Exception e) {
                log.error(e,"Server: [{}] 启动失败 !!\n", name);
                System.exit(-1);
            }
        }
    }

    private void clear(){
        this.noLoadKits.clear();
        this.contextFunction = null;
        this.startEvents.clear();
    }

    /**
     * 停止
     */
    public synchronized void stop() {
        if (status.compareAndSet(true, false)) {
            try {
                Map<String, LifeCycle> beansOfType = context.getBeansOfType(LifeCycle.class);
                for (LifeCycle lifeCycle : beansOfType.values()) {
                    lifeCycle.destroy();
                }
                for (IKit iKit : moduleMap.values()) {
                    iKit.shutdown();
                }
                ExecutorManager.destroy();
            } catch (Exception e) {
                log.error(e,"Server: {} stop error!!", name);
            }
        }
    }

    private IKit loadModule(Kit kit) {
        IKit iKit = ReflectUtil.newInstance(kit.getCls());
        iKit.injectConf(Conf.get().getSetting(kit.name()));
        iKit.init();
        return iKit;
    }

    //重载配置conf
    private void reloadConf() {
        for (Map.Entry<Kit, IKit> entry : moduleMap.entrySet()) {
            entry.getValue().injectConf(Conf.get().getSetting(entry.getKey().name()));
        }
    }

    public ApplicationContext getSpringContext() {
        return context;
    }
}
